package repo

import (
	"fmt"
	"os"
	"path"
	"regexp"
	"strings"
)

const (
	BUILTIN_TRAIT_WAY = "way" // 方式
	BUILTIN_TRAIT_TAG = "tag" // 标签
)

const (
	TRAIT_WAY_SRCPKG  = "srcpkg"  // 源码包
	TRAIT_WAY_SRCREPO = "srcrepo" // 源码仓库
	TRAIT_WAY_PREBIN  = "prebin"  // 预编译二进制包

	TRAIT_WAY_DEFAULT = TRAIT_WAY_SRCPKG
)

var TraitNamePattern = regexp.MustCompile("[a-zA-Z_][0-9a-zA-Z_]*")

type ModuleTrait struct {
	Key string
	Val string
}

func (t ModuleTrait) String() string {
	var buf strings.Builder
	buf.WriteString(t.Key)
	buf.WriteRune(rune('='))
	buf.WriteString(t.Val)
	return buf.String()
}

// ModuleLabel 定义了一个模块标签对象
type ModuleLabel struct {
	ModuleName          string                  `json:"name"`    // 模块名称
	ModuleVersion       string                  `json:"version"` // 模板版本号
	ModuleTraits        []*ModuleTrait          `json:"traits"`
	ModuleTraitsMapping map[string]*ModuleTrait `json:"-"`
}

func (l ModuleLabel) String() string {
	var buf strings.Builder
	buf.WriteString(l.ModuleName)
	buf.WriteRune('/')
	buf.WriteString(l.ModuleVersion)

	if len(l.ModuleTraits) > 0 {
		buf.WriteString("?")
		for i := 0; i < len(l.ModuleTraits); i++ {
			if i != 0 {
				buf.WriteRune(rune('&'))
			}
			buf.WriteString(l.ModuleName)
			buf.WriteRune(rune('='))
			buf.WriteString(l.ModuleVersion)
		}
	}

	return buf.String()
}

// ParseModuleLabel 用于将模块标签拆成模块和版本两个部分
func ParseModuleLabel(modver string) (*ModuleLabel, error) {
	moduleName := ""
	moduleVersion := ""
	moduleTraits := make([]*ModuleTrait, 0, 64)
	moduleTraitsMapping := make(map[string]*ModuleTrait)

	// 检查 modver 格式
	remain := modver

	// ModuleName
	items := strings.Split(remain, "/")
	if len(items) == 2 {
		moduleName = strings.TrimSpace(items[0])
		remain = strings.TrimSpace(items[1])
	} else {
		return nil, fmt.Errorf("the format of module-version string is invalid: `%v'", modver)
	}

	// ModuleVersion
	items = strings.Split(remain, "?")
	if len(items) == 1 {
		moduleVersion = strings.TrimSpace(items[0])
		remain = ""
	} else if len(items) == 2 {
		moduleVersion = strings.TrimSpace(items[0])
		remain = strings.TrimSpace(items[1])
	} else {
		return nil, fmt.Errorf("the format of module-version string is invalid: `%v'", modver)
	}

	if len(moduleName) == 0 {
		return nil, fmt.Errorf("the module name is missing: `%v'", modver)
	}

	if len(moduleVersion) == 0 {
		return nil, fmt.Errorf("the module version is missing: `%v'", modver)
	}

	_, err := TokenDecode(moduleName)
	if err != nil {
		return nil, fmt.Errorf("invalid module name '%v': %v", moduleName, err)
	}

	_, err = TokenDecode(moduleVersion)
	if err != nil {
		return nil, fmt.Errorf("invalid module name '%v': %v", moduleVersion, err)
	}

	// ModuleTraits
	if len(remain) > 0 {
		items = strings.Split(remain, "&")
		for _, item := range items {
			pair := strings.Split(item, "=")
			if len(pair) != 2 {
				return nil, fmt.Errorf("the format of module-version string is invalid: `%v'", modver)
			}

			if _, exist := moduleTraitsMapping[pair[0]]; exist {
				return nil, fmt.Errorf("duplicate trait '%v' in '%v'", pair[0], modver)
			}

			trait := &ModuleTrait{
				pair[0],
				pair[1],
			}

			_, err := TokenDecode(pair[1])
			if err != nil {
				return nil, fmt.Errorf("invalid module trait '%v': %v", pair[1], err)
			}

			moduleTraits = append(moduleTraits, trait)
			moduleTraitsMapping[trait.Key] = trait
		}
	}

	return &ModuleLabel{moduleName, moduleVersion, moduleTraits, moduleTraitsMapping}, nil
}

func ListModuleLabels(repoDir string, statusFile string) ([]*ModuleLabel, error) {
	labelList := make([]*ModuleLabel, 0, 100)

	moduleDirs, err := os.ReadDir(repoDir)
	if err != nil {
		return nil, fmt.Errorf("%v", err.Error())
	}

	// 遍历模块目录
	for _, moduleDir := range moduleDirs {
		// 如果不是目录就跳过
		if !moduleDir.IsDir() {
			continue
		}

		// 如果目录名是以点开头，全部忽略
		if strings.HasPrefix(moduleDir.Name(), ".") {
			continue
		}

		// 如果遇到@开头的模块说明是子模块
		//if strings.HasPrefix(moduleDir.Name(), "@") {
		//	moduleDomain := moduleDir.Name()
		//	childModuleDirs, err := os.ReadDir(repoDir)
		//	if err != nil {
		//		continue
		//	}
		//
		//	for _, childModuleDir := range childModuleDirs {
		//		scanModuleVersions(&labelList, statusFile, path.Join(repoDir, moduleDomain, childModuleDir.Name()), moduleDomain)
		//	}
		//} else {
		scanModuleVersions(&labelList, statusFile, path.Join(repoDir, moduleDir.Name()))
		//}
	}

	return labelList, nil
}

func scanModuleVersions(labelList *[]*ModuleLabel, statusFile string, moduleDir string) error {
	moduleName := path.Base(moduleDir)

	// 列出版本目录
	versionDirs, err := os.ReadDir(moduleDir)
	if err != nil {
		return fmt.Errorf("%v", err.Error())
	}

	// 遍历版本目录
	for _, versionDir := range versionDirs {
		// 如果不是目录就调过
		if !versionDir.IsDir() {
			continue
		}

		// 如果存在 .thpkg-installed，说明已经找到一个模块的版本
		if !PathIsFile(path.Join(moduleDir, versionDir.Name(), statusFile)) {
			continue
		}

		labelString := fmt.Sprintf("%v/%v", moduleName, versionDir.Name())

		moduleLabel, err := ParseModuleLabel(labelString)
		if err != nil {
			return err
		}

		// 保存模块版本
		*labelList = append(*labelList, moduleLabel)
	}

	return nil
}

var encodeMapping = map[rune]string{
	rune('?'):  "%3f",
	rune(':'):  "%3a",
	rune('"'):  "%22",
	rune('\''): "%27",
	rune('\\'): "%5c",
	rune('/'):  "%2f",
	rune('%'):  "%25",
	rune(' '):  "%20",
}

var decodeMapping = map[string]rune{
	"%3F": rune('?'),
	"%3f": rune('?'),
	"%3A": rune(':'),
	"%3a": rune(':'),
	"%22": rune('"'),
	"%27": rune('\''),
	"%5C": rune('\\'),
	"%5c": rune('\\'),
	"%2F": rune('/'),
	"%2f": rune('/'),
	"%25": rune('%'),
	"%20": rune(' '),
}

func IsValidTraitName(name string) bool {
	return TraitNamePattern.Match([]byte(name))
}

// TokenEncode 对一个 token 进行编码
func TokenEncode(token string) string {
	builder := strings.Builder{}

	for _, ch := range token {
		val, ok := encodeMapping[ch]
		if ok {
			builder.WriteString(val)
			continue
		}
		builder.WriteRune(ch)
	}

	return builder.String()
}

// TokenDecode 对一个 token 进行解码
func TokenDecode(token string) (string, error) {
	result := strings.Builder{}

	keyBuffer := strings.Builder{}

	st := 0
	for index, ch := range token {
		switch st {
		case 0:
			// 遇到转义符号
			if ch == '%' {
				keyBuffer.WriteRune(ch)
				st = 1
				continue
			}

			result.WriteRune(ch)
			continue
		case 1:
			keyBuffer.WriteRune(ch)
			st = 2
			continue
		case 2:
			keyBuffer.WriteRune(ch)

			skey := keyBuffer.String()
			r, ok := decodeMapping[skey]
			if !ok {
				return "", fmt.Errorf("unsupported decode sequence '%v' at %v", skey, index)
			}

			result.WriteRune(r)
			keyBuffer.Reset()
			st = 0
			continue
		}
	}

	if st != 0 {
		return "", fmt.Errorf("uncompleted escape sequence near '%v'", keyBuffer.String())
	}

	return result.String(), nil
}
